/*
 Developer: John Archer (john.archer.jpa@gmail.com)
 
 This file is part of VTBuilder.

 VTBuilder is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License.

 VTBuilder is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with VTBuilder.  If not, see <http://www.gnu.org/licenses/>.
 */
package core.tasks.pack;

import core.para.Para;
import core.rb.r.MR;
import gui.processor.Processor;
import gui.utils.ProgBar;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class PairCheckPack extends Thread {

    public int[][] pOut;
    private final CountDownLatch stopLatch;
    private List<String> pIn_fw;
    private List<String> pIn_rev;
    private HashMap<String, ArrayList<Integer>> lib;
    private Processor p;
    private int refLen;
    private Random rand;

    public PairCheckPack(CountDownLatch stopLatch, List<String> pIn_fw, List<String> pIn_rev, HashMap<String, ArrayList<Integer>> l, int rLen, Processor p) {
        this.stopLatch = stopLatch;
        this.pIn_fw = pIn_fw;
        this.pIn_rev = pIn_rev;
        this.lib = l;
        this.refLen = rLen;
        rand = new Random();
        this.p = p;
    }

    public void run() {
        if (p == null) {
            pOut = checkPairs();
        } else {
            pOut = checkPairsWithProgress();
        }
        stopLatch.countDown();
    }

    private int[][] checkPairs() {
        int[][] hits = new int[2][refLen];
        for (int x = 0; x < pIn_fw.size(); x++) {
            doMappingMatch(pIn_fw.get(x));
            if (mr != null) {
                int sF = mr.s;
                hits[0][sF]++;
                doMappingMatch(pIn_rev.get(x));
                if (mr != null) {
                    if (mr.s > sF && (mr.s - sF) < 1000) {
                        hits[1][sF]++;
                    }
                }
            }
        }
        return hits;
    }

    private MR mr;
    private int[][] checkPairsWithProgress() {
        int[][] hits = new int[2][refLen];
        if(Para.GUI){p.dPrgA(ProgBar.START, pIn_fw.size());}
        
        DecimalFormat format = null;
        if(Para.GUI){p.dPrgA(ProgBar.START, pIn_fw.size());}
        else{format = new DecimalFormat("##.##");}
        
        int tenPercentOfReads = (int)(pIn_fw.size() * 0.1);
        
        for (int x = 0; x < pIn_fw.size(); x++) {
            doMappingMatch(pIn_fw.get(x));
            if (mr != null) {
                int sF = mr.s;
                hits[0][sF]++;
                doMappingMatch(pIn_rev.get(x));
                if (mr != null) {
                    if (mr.s > sF && (mr.s - sF) < 1000) {
                        hits[1][sF]++;
                    }
                }
            }
            if(Para.GUI){p.dPrgA(ProgBar.UPDATE, x);}
            
            
            if(Para.GUI){if(x%tenPercentOfReads==0){p.dPrgA(ProgBar.UPDATE, x);}}
            else{if(x%tenPercentOfReads==0){System.out.print("\r pairwise check: "+ format.format(((double)x/pIn_fw.size()) * 100) + "%");}}
            
            
            
        }
        if(Para.GUI){p.dPrgA(ProgBar.END, 0);}
        else{System.out.println("\n\n");}
        return hits;
    }

    private void doMappingMatch(String dna) {
        TRHit[] siteHt = new TRHit[dna.length() - 10];
        int df = 0;int s = 0;int hts = 0;
        ArrayList<TRHit>[] sd = new ArrayList[siteHt.length];
        for (int r = 0; r < sd.length; r++) {
            sd[r] = new ArrayList<TRHit>();
        }
        HashMap<Integer, Integer> hsh = new HashMap<Integer, Integer>(250);
        for (int k = 0; k < siteHt.length; k++) {
            if (lib.containsKey(dna.substring(k, k + 10))) {
                String kmer = dna.substring(k, k + 10);
                for (int t : lib.get(kmer)) {
                    sd[k].add(new TRHit(t, k, df = t - k));
                    if(hsh.get(df)==null){hsh.put(df, 1);}
                    else{hsh.put(df, hsh.get(df)+1);}
                }
            }
        }
        ArrayList<Integer> mfreqs = new ArrayList<Integer>();
        ArrayList<Integer> mdiffs = new ArrayList<Integer>();
        mfreqs.add(0);
        mdiffs.add(0);
        for (Map.Entry<Integer, Integer> e : hsh.entrySet()) {
            if (e.getValue() > mfreqs.get(0)) {
                //this is the highest - clear the rest of the list
                mfreqs.clear();
                mdiffs.clear();
                mfreqs.add(e.getValue());
                mdiffs.add(e.getKey());
            } else if (e.getValue() == mfreqs.get(0)) {
                //equal so add it
                mfreqs.add(e.getValue());
                mdiffs.add(e.getKey());
            }
        }
        //if multiple hits that are the same take a random one
        //need to work out a random diff to use
        int mD = mdiffs.get(0);
        if (mdiffs.size() > 1) {
            mD = mdiffs.get(rand.nextInt(mfreqs.size()));
        }
        for (int c = 0; c < sd.length; c++) {
            inner:
            for (int i = 0; sd[c] != null && i < sd[c].size(); i++) {
                if (sd[c].get(i).df == mD) {
                    siteHt[c] = sd[c].get(i);
                    break inner;
                }
            }
        }
        
        mr = null;
        s = hts = 0;
        while (s < siteHt.length) {
            if (siteHt[s++] != null) {
                hts++;
            }
        }
        if (hts > (int) (siteHt.length * 0.1)) {
            int swin = 0;
            while (siteHt[swin] == null) {
                swin++;
            }
            dna = dna.substring(siteHt[swin].rl);
            mr = new MR(dna, siteHt[swin].tl, siteHt[swin].tl + dna.length());
        }
        
    }


}